# coding:utf-8
import unittest
# from HtmlTestRunner import HTMLTestRunner
from ycase import BASE_YAML_SCHEMA, BASE_RESPONSE_SCHEMA
from dotmap import DotMap
from requests import Session, Request, Response
from path import Path
import json
from ycase.HTMLTestReportCN import HTMLTestRunner


def _yaml_load(file_path):
    import yaml
    with open(file_path, mode='r', encoding='utf-8') as fs:
        return yaml.load(fs.read())


def _validate_schema(data, schema, required=True):
    if isinstance(data, dict) and isinstance(schema, dict):
        try:
            from voluptuous import Schema
            sc = Schema(schema, required=required)
            sc(data)
            return True
        except Exception:
            return False


class YStep(object):
    """[summary]

    Arguments:
        object {[type]} -- [description]
    """

    def __init__(self, data, env):
        self._data = data
        self.name = self._data.get('name')
        self.path = env + self._data.get('path')
        self.method = self._data.get('method')
        self.headers = self._data.get('headers')
        self.params = self._data.get('params')
        self.enablevalidate = self._data.get('enablevalidate')
        self.expected = self._data.get('expected')

    @property
    def http_request(self):
        if str(self.method).upper() == 'GET':
            return Request(
                method=self.method,
                headers=self.headers,
                url=self.path,
                params=self.params,
            )
        elif str(self.method).upper() == 'POST':
            return Request(
                method=self.req_method,
                headers=self.headers,
                url=self.path,
                json=self.params,
            )
        else:
            return None


class YCase(unittest.TestCase):

    http_session = None

    def __init__(self, file_path, schema=BASE_YAML_SCHEMA):
        try:
            super().__init__(methodName='runTest')
            self._data_dict = _yaml_load(file_path)
            self._file_name = file_path.namebase + file_path.ext
            if not _validate_schema(self._data_dict, schema):
                raise Exception()
            self.name = self._data_dict.get('ycase').get('name')
            self.env = self._data_dict.get('ycase').get('env')
            self.tester = self._data_dict.get('ycase').get('tester')

        except Exception as ex:
            # TODO 抛出自定义异常
            raise ex('初始化失败')

    @property
    def data_dotmap(self):
        return DotMap(self._data_dict)

    # base testcase

    @classmethod
    def setUpClass(self):

        if self.http_session is None:
            self.http_session = Session()

    @classmethod
    def tearDownClass(self):
        if self.http_session is not None:
            self.http_session.close()

    def runTest(self):
        for step in self._data_dict.get('ycase').get('steps'):
            ystep = YStep(step, self.env)
            resp = self.http_session.send(ystep.http_request.prepare())

            self.assertTrue(resp.ok,
                            msg='HTTP 相应状态错误-[{}-{}]'.format(self.name, ystep.name))

            if ystep.enablevalidate:
                # 返回结构校验
                resp_dict = json.loads(resp.text)
                is_schema_done = _validate_schema(
                    resp_dict, BASE_RESPONSE_SCHEMA)
                self.assertTrue(is_schema_done,
                                msg='返回结构校验失败-[{}-{}]'.format(self.name, ystep.name))
                # 返回值校验
                self.assertDictEqual(ystep.expected, resp_dict,
                                     msg='返回值校验失败-[{}-{}]'.format(self.name, ystep.name))


class YSuite(object):

    def __init__(self, dir_path):
        self._dir_path = dir_path

    def run(self):
        ts = unittest.TestSuite()
        for f in Path(self._dir_path).files('*.yaml'):
            ts.addTest(YCase(f))
        for f in Path(self._dir_path).files('*.yml'):
            ts.addTest(YCase(f))
        title = '自动化测试报告'
        runner = HTMLTestRunner(
            title=title,
            tester=ts._tests[0].tester if len(ts._tests) > 0 else None
        )
        runner.run(ts)
